#include <stdio.h>
#include <glib.h>
#include <kewinternal.h>

extern KewObjectType real_type;

#define source_file_name "Real"

/* FIXME: this should be done like Gtk */
typedef struct {
  KewObject *object;
  double value;
} RealObject;

#define value(REALOBJECT) (((RealObject*)REALOBJECT)->value)
#define check_real(VAL) KewCheckType((VAL), &real_type, "real number")


#define DEFINE_REAL_OP(OPNAME,OP) \
METHOD(OPNAME) \
{ \
  check_real(args[0]); \
  results[0] = KewNewReal(value(receiver) OP value(args[0]));\
}

#define DEFINE_BOOL_OP(OPNAME,OP) \
METHOD(OPNAME) \
{ \
  check_real(args[0]); \
  results[0] = KewNewBool(value(receiver) OP value(args[0])); \
}

DEFINE_REAL_OP(Plus,+)
DEFINE_REAL_OP(Minus,-)
DEFINE_REAL_OP(Multiply,*)
DEFINE_REAL_OP(Divide,/)

DEFINE_BOOL_OP(Less,<)
DEFINE_BOOL_OP(LessEqual,<=)
DEFINE_BOOL_OP(Greater,>)
DEFINE_BOOL_OP(GreaterEqual,>)
DEFINE_BOOL_OP(Equal,==)
DEFINE_BOOL_OP(NotEqual,!=)

METHOD(Max)
{
  check_real(args[0]);
  results[0] = KewNewReal(MAX(value(receiver), value(args[0])));
}

METHOD(Min)
{
  check_real(args[0]);
  results[0] = KewNewReal(MIN(value(receiver), value(args[0])));
}

METHOD(Abs)
{
  results[0] = KewNewReal(ABS(value(receiver)));
}

METHOD(Clamp)
{
  check_real(args[0]);
  check_real(args[1]);
  results[0] = KewNewReal(CLAMP(value(receiver), value(args[0]), value(args[1])));
}

METHOD(Hash)
{
  results[0] = KewNewInt((int)value(receiver));
}

METHOD(String)
{
  results[0] = KewWrapString(g_strdup_printf("%f", value(receiver)));
}

METHOD(Real)
{
  results[0] = receiver;
}

static KewObjectType real_type = {
  source_file_name, __LINE__,
  2, /* FIXME: this should not be a guess of how many context slots fit a double!! */
  {
    {"hash", Hash, 0, 1},
    {"+", Plus, 1, 1},
    {"-", Minus, 1, 1},
    {"*", Multiply, 1, 1},
    {"/", Divide, 1, 1},
    {"<", Less, 1, 1},
    {"<=", LessEqual, 1, 1},
    {">", Greater, 1, 1},
    {">=", GreaterEqual, 1, 1},
    {"=", Equal, 1, 1},
    {"!=", NotEqual, 1, 1},
    {"max", Max, 1, 1},
    {"min", Min, 1, 1},
    {"abs", Abs, 0, 1},
    {"clamp", Clamp, 2, 1},
    {"string", String, 0, 1},
    {"real", Real, 0, 1},
    {NULL, NULL, 0, 0}
  }
};


KewObject *
KewNewReal(double f)
{
  KewObject *result;

  result = KewNewObject(&real_type);
  value(result) = f;
  return result;
}

double
KewRealVal(KewObject *object)
{
  /* FIXME: raise an exception if this object is not an float */
  return value(object);
}
